package com.example.cracker;

import java.math.BigInteger;

public class Challenge4Verifier {

	private int[] h;
	private int locationX;
	private int locationY;
	private int[][] matrix;
	private int[][] matrixCount;
	private int matrixSize;
	private StringBuilder nameBuffer;
	private int nameOffset;
	private int steps;
	private int[] v;

	public Challenge4Verifier() {
		h = new int[] { 0x2, 0x2, -0x2, -0x2, 0x1, 0x1, -0x1, -0x1 };
		v = new int[] { 0x1, -0x1, 0x1, -0x1, 0x2, -0x2, 0x2, -0x2 };
	
		locationX = 0;
		locationY = 0;
		steps = 0;
		nameBuffer = new StringBuilder();
		nameOffset = 0;
	}
	
	private int getNextLocation() {
		int bestMove = -1;
		int smallestCount = 0x9;
		int moveCount = 0;	
		
		for (int i = 0; i < 8; ++i) {
			if ((isValidLocation(locationX, locationY, i)) && matrix[h[i] + locationX][v[i] + locationY] == 0) {
				moveCount = matrixCount[locationX + h[i]][locationY + v[i]];
				if (moveCount < smallestCount) {
					smallestCount = moveCount;
					bestMove = i;
				} else if(moveCount == smallestCount && getTieBreaker()) {
					bestMove = i;
				}
			}
		}
		return bestMove;
	}

	private boolean isValidLocation(int x, int y, int move) {
		if (
				((h[move] + x) >= matrixSize) || 
				((h[move] + x) < 0) || 
				((v[move] + y) >= matrixSize) || 
				((v[move] + y) < 0)) {
			return false;
		}
		return true;
	}
	
	private boolean getTieBreaker() {
		int temp = nameBuffer.charAt(nameOffset);
		nameOffset += 1;
		temp -= 0x60;
		temp = temp % 2;
		if (temp != 1) {
			return false;
		} else {
			return true;
		}
	}
	
	private void setLocation(int x, int y) {
		steps += 1;
		
		for (int z = 0; z < 8; ++z) {
			if (isValidLocation(x, y, z)) {
				int tempX = h[z] + x;
				int tempY = v[z] + y;
				
				matrixCount[tempX][tempY] -= 0x1;	
			}
		}
		matrix[x][y] = steps;
		locationX = x;
		locationY = y;
	}
	
	
	private int populateMatrix() {
		initMatrix();
		
		int num = nameBuffer.charAt(nameOffset);
		nameOffset += 0x1;
		int startX = num % matrixSize;
		
		num = nameBuffer.charAt(nameOffset);
		nameOffset += 0x1;
		int startY = num % matrixSize;
		
		setLocation(startX, startY);
		while (getNextLocation() != -0x1) {
			int bestLoc = getNextLocation();
			int x = locationX + h[bestLoc];
			int y = locationY + v[bestLoc];
			setLocation(x, y);
		}
		
		return steps;
	}
	
	private void initMatrix() {
		matrix = new int[matrixSize][matrixSize];
		matrixCount = new int[matrixSize][matrixSize];
		
		for (int x = 0; x < matrixSize; ++x) {
			for (int y = 0; y < matrixSize; ++y) {
				matrix[x][y] = 0;
			}
		}
		
		for (int x = 0; x < matrixSize; ++x) {
			for (int y = 0; y < matrixSize; ++y) {
				for (int z = 0; z < 8; ++z) {
					if (isValidLocation(x, y, z)) {
						matrixCount[x][y] = matrixCount[x][y] + 0x1;
					}
				}
			}
		}
	}
	
	private static String intArrayToHexString(int[] ints) {
		StringBuilder sb = new StringBuilder();

		for (int x = 0; x < ints.length; ++x) {
			sb.append(String.format("%04X", new Object[]{Integer.valueOf(ints[x])} ));
		}
		return sb.toString();
	}
	
	
	public String generator(String name) {
		name = name.toLowerCase();
		
		int count = 0;
		while (nameBuffer.length() < 0x1b58) {
			nameBuffer.append(name);
			++count;
		}
		matrixSize = count % 0x31 + 9;
		
		initMatrix();
		populateMatrix();
		
		BigInteger[] bigInts = new BigInteger[matrixSize];
		for (int x = 0; x < matrixSize; ++x) {
			String str = intArrayToHexString(matrix[x]);
			bigInts[x] = new BigInteger(str, 0x10);
			
			if (x > 1) {
				bigInts[x] = bigInts[x].multiply(bigInts[x - 2]);
			}
			
			if (x > 0) {
				bigInts[x] = bigInts[x].xor(bigInts[x - 1]);
			}
		}
		
		BigInteger v7 = bigInts[matrixSize - 0x1];
		BigInteger v8 = new BigInteger("100000000000000000000");

		BigInteger bigOne = new BigInteger(v7.mod(v8).toString());
		
		return bigOne.toString();
	}
	
	
}































